C#进阶(上)

本篇章内容知识偏多,因此分三次上传。

简单数据结构类

ArrayList

ArrayList的本质

ArrayList是一个C#为我们1封装好的的类,本质是一个object类型的数组,ArrayList类帮助我们实现很多方法,比如数组的删减查改。

申明

1
2
using System.Collections
ArrayList array = new ArrayList();

增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
array.Add(1);//增
array.AddRange(array2);//范围增加(批量增加1)
array.Remove(1);//从头找,找到删
array.Insert(1,"1234567");///插入

array.RemoveAt(2)//删除某一位置的元素
array.Clear();//清空

arrary[0];//表示第几个元素
if(array.Contains("bababoi"))
{
//判断有无元素,有则进入,无则不进入
}
int idx;
idx = array.IndexOf(true);//正向查找元素位置,返回值是位置,找不到返回-1
idx = array.LastIndexOf(true);//反向查找元素位置,返回的是位置

array[0] = "999";//改

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
int l = array.Count;//表示长度
int r = array.Capacity;//表示容量,避免产生过多的垃圾

for(int i = 0; i < l; i++)
{
Console.WriteLine(array[i]);
}

//迭代器遍历
foreach(var item in array)
{
Console.WriteLine(item);
}

装箱拆箱

ArrayList本质是一个可以自动扩容的object数组,由于万物之父用来储存数据,自然存在装箱拆箱。所以ArrayList尽量少用,后期会有一个更好的数据容器。

Stack

Stack的本质

Stack是一个c#封装好的类,它的本质是object数组,只是封装了特殊的存储规则。
Stack是栈存储容器,栈是一种先进后出的数据结构。可以类似看作是一个水桶。

申明

1
2
using System.Collections;
Stack stack = new Stack();

增取查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
stack.Push(1);//压栈

object v = stack.Pop();
stack.Pop();//弹栈

//栈无法查看指定位置的元素,只能查看栈顶的元素
v = stack.Peek();
//查看元素是否处于栈中
if(stack.Contains(1))
{
//存在即进入,不存在即不进入
}

//栈无法改元素,只有压栈与弹栈两种操作,实在要改,只能清空。
stack.Clear();

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int l = stack.Count;//长度

//用foreach遍历,顺序为从栈顶到栈底。
foreach(object item in stack)
{
Console.WriteLine(item);
}

//还有一种遍历方式,就是将栈转换为object数组,遍历出的顺序还是从栈顶到栈底
object[] array = stack.ToArray();
for(int i = 0; i < l; i++)
{
Console.WriteLine(array[i]);
}

//循环弹栈
while(stack.Count > 0)
{
object o = stack.Pop();
Console.WriteLine(o);
}

装箱拆箱

与ArrayList一致。

Queue

Queue本质

Queue是一个c#封装好的类,它的本质是object数组,只是封装了特殊的存储规则。
Queue是队列存储容器,队列是一种先进先出、后进后出的数据结构。可以类似看作是一个通道。

申明

1
2
using System.Collections;
Queue queue = new Queue();

增取查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
queue.Enqueue(1);//入队

//队列中不存在删除的概念,只有取得概念,出队
object v = queue.Dequeue();
queue Dequeue();

//查看队列头部元素但是不会移除
v = queue.Peek();

//查看元素是否处于队列中
if(queue.Contains(1))
{
//与前俩一致
}

//队列无法改,只能进出,与栈相同

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int l = queue.Count;//长度

//用foreach遍历
foreach(object item in queuue)
{
//与栈一致
}

object[] array = queue.ToArray();
for(int i = 0; i < l; i++)
{
//与栈一致
}

//循环出队
while(queue.Count > 0)
{
object o = queue.Dequeue();
}

装箱拆箱

与栈一致。

Hashtable

Hashtable的本质

又称散列表,是基于键的哈希代码组织起来的键/值对,它的主要作用是提高数据查询的效率,使用键来访问集合中的元素。

申明

1
2
using System.Collections;
Hashtable hashtable = new Hashtable();

增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
hashtable.Add(1, "123");//增
//不能出现相同的键

//只能通过键去删
hashtable.Remove(1);
//删除不存在的键没反应
//或者可以直接清空
hashtable.Clear();

//通过键去查看值,找不到会返回空
hashtable[1];
//查看是否存在
//通过键检测
if(hashtable.Contains(1))
{

}
if(hashtable.ContainsKey(1))
{

}
//通过值检测
if(hashtable.ContainsValuue("123"))
{

}

//只能改键对应的值内容,无法修改键
hashtable[1] = "321";

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int l = hashtable.Count;//对数

//遍历所有键
foreach(object item in hashtable.Keys)
{

}
//遍历所有值
foreach(object item in hashtable.Values)
{

}
//键值对一起遍历
foreach(DictionaryEntry item in hashtable)
{
Console.WriteLine(item.Key + item.Value);
}
//迭代器遍历法
IDictionaryEnumerator myEnumerator = hashtable.GetEnumerator();
bool flag = myEnumerator.MoveNext();
while(flag)
{
Console.WriteLine(myEnumerator.Key + myEnumerator.Value);
flag = myEnumerator.MoveNext();
}

装箱拆箱

与前面的一样。

泛型

泛型

是什么

泛型实现了类型参数化,达到代码重用的目的,通过类型参数化来实现同一份代码上操作多种类型。
泛型相当于类型占位符,定义类或方法时使用替代符代表变量类型,当真正使用类或方法时再具体指定类型。

泛型分类

泛型类和泛型接口:

1
2
class 类名<泛型占位字母>
interface 接口名<泛型占位字母>

泛型函数

1
函数名<泛型占位字母>(参数列表)

泛型占位字母可以有多个,用逗号隔开。

泛型类和接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class TestClass<T>
{
public T value;
}
TestClass<int> t = new TestClass<int>();
t.value = 10;
TestClass<string> t2 = new TestClass<int>();
t2.value = "1100";

class Test<T, K, L>
{
public T value1;
public K value2;
public L value3;//注意这里的变量名不能重复
}

Test<int, string, bool> t3 = new Test<int, string, bool>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Test<T>
{
T Value
{
get;
set;
}
}
class Test : TestInterface<int>
{
public int Value
{
get;
set;
}
}

泛型方法

普通类中的泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Test2
{
public void TestFun<T>( T value)
{

}
public void TestFun<T>()
{
//用泛型类型进行逻辑处理
T t = default(T);
}
public T TestFun<T>(string v)
{
//用泛型类型进行逻辑处理
T t = default(T);
return t;
}
}
Test2 tt = new Test2();
tt.TestFun<string>("123");

泛型类中的泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Test2<T>
{
public T value;
//这个是泛型方法
public void TestFun<M>(M m)
{

}
//这个不叫泛型方法,因为T是泛型类申明的时候就指定的,在使用的时候不能动态改动。
public void TestFun(T t)
{

}
}
Test2<int> tt = new Test2<int>();
tt.TestFun(10);
tt.TestFun<string>("123");

泛型作用

  • 不同类型对象的相同逻辑处理可以使用泛型
  • 使用泛型可以有效避免装箱拆箱

例如:

1
2
3
4
5
6
7
8
9
10
11
12
class ArrayList<T>
{
private T[] array;
public void Add(T value)
{

}
public void Remove(T value)
{

}
}

泛型约束

什么是泛型约束

让泛型的类型有一定的限制,关键字:where;泛型约束有6种:

  • 值类型: where 泛型字母:struct
  • 引用类型: where 泛型字母: class
  • 存在无参公共构造函数: where 泛型字母:new()
  • 某个类本身或者其派生类(某个接口的派生类型): where 泛型字母:接口名
  • 另一个泛型类型本身或者派生类型: where 泛型字母:另一个泛型字母

各个泛型类型的解释

值类型约束

1
2
3
4
5
6
7
8
class Test1<T> where T : struct
{
public T value;
public void TestFun<K>(K v) where K : struct
{

}
}

引用类型约束

1
2
3
4
5
6
7
8
class Test2<T> where T : class
{
public T value;
public void TestFun<K>(K v) where K : class
{

}
}

无参公共构造函数约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Text3<T> where T : new()
{
public T value;
public void TestFun<K>(K v) where K : new()
{

}
}
class Test1
{

}
class Test2
{
public void Test2(int a)
{

}
}
Test3<Test1> t3 = new Test3<Test1>();

类约束:必须是该类或者其派生类

1
2
3
4
5
6
7
8
class Text4<T> where T : Test1
{
public T value;
public void TestFun<K>(K v) where K : Test1
{

}
}

接口约束:接口的派生类型

1
2
3
4
5
6
7
8
9
10
11
12
interface IFly
{

}
class Text5<T> where T : IFly
{
public T value;
public void TestFun<K>(K v) where K : IFly
{

}
}

另一个泛型类型约束

1
2
3
4
5
6
7
8
class Test6<V,U> where : V : U//要么一样要么是派生类型
{
ppublic V value;
public void TestFun<T,K>(K k) where K : T
{

}
}

多个泛型有约束

同一种泛型多约束加逗号。不同泛型直接写where

1
2
3
4
class Test8<T,K> where T : class, new() where K : struct
{

}

常用泛型数据结构类

List

List的本质

List是c#为我们封装好的额一个类,它的本质是一个可变类型的泛型数组,List类为我们实现很多方法,比如泛型数组的增删查改。

申明

1
2
using System.Collections.Generic;
List<int> list = new Lies<int>();

增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
list.Add(1);//增
list.AddRange();//用法与arraylit一样

list.Insert(0, 999);//插入

list.Remove(1);//删
list.RemoveAt(0);
list.Clear();

int i = list[0];//查
if(list.Contains(1))
{
//判断是否存在
}
int idx = list.IndexOf(1);//正向查找
int idx2 = list.LastIndexOf(1);//反向查找

list[0] = 2;//改

遍历

1
2
3
4
5
6
7
8
9
int l = list.Count;//长度
for(int i 0; i < l; i++)
{
Console.WriteLine(list[i]);
}
foreach(int item in list)
{
Console.WriteLine(item);
}

Dictionarry

Dictionary的本质

可以将Dictionary理解成拥有泛型的Hashtable,它是基于键的哈希代码组织起来的键值对,键值对类型从Hashtable的object变为了可以自己定制的泛型。

申明

1
2
3
using System.Collections.Generic
Dictionary<int,string> dictionary = new Dictionary<int,string>();

增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//不能出现相同的键,值可以一致
dictionary.Add(1,"123");//增

//只能通过键去删除
dictionary.Remove(1);//删
dictionary.Clear();

//通过键来查看
string s = dictionary[1];//查
//查看是否存在
if(dictionary.ContainsKey(1))
{

}
if(dictionary.ContainsValue("123"))
{

}

dictionary[1] = "567";//改

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int l = dictionary.Count;//容量
foreach(int item in dictionary.Count)
{
Console.WriteLine(item);
Console.WriteLine(dictionary[item]);
}
foreach(string item in dictionary.Values)
{
Console.WriteLine(item);
}
for(KeyValuePair<int, string> item in dictionary)
{
Console.WriteLine(item.Key + ":" item.Value);
}

顺序存储和链式存储

数据结构

数据结构是计算机存储、组织数据的方法,数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。比如自定义一个类也可以成为一种数据结构,自己定义的数据结构组合规则。

线性表

线性表是一种数据结构,是由n个具有相同的特征的数据元素的有限序列。

顺序存储

数组、Stack,Queue,List,ArrayList都是顺序存储,只是组织规律不同罢了。
顺序存储:用一组地址连续的存储单元依次存储线性表的各个元素。

链式存储

单向链表、双向链表、循环链表
链式存储:用一组任意的存储单元存储线性表中的各个元素。

实现单向链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class LinkedNode<T>
{
public T value;
public LinkedNode<T> nextNode;
public LinkedNode(T value)
{
this value = value;
}
}
class LinkedList<T>
{
public LinkedNode<T> head;
public LinkedNode<T> last;
public void Add(T value)
{
LinkNode<T> node = new LinkedNode<T>(value);
}
if(head == null)
{
head = node;
last = node;
}
else
{
last.nextNode = node;
last = node;
}
public void Remove(T value)
{
if(head == null)
{
return;
}
if(head.value.Equals(value))
{
head = head.nextNode;
if(head == null)
{
last = null;
}
return;
}
LinkedNode<T> node = head;
while(node.nextNode !!= null)
{
if(node.nextNode.value.Equals(value))
{
node.nextNode = node.nextNode.nextNode;
break;
}
}
}
}

顺序存储和链式存储的优缺点

从增删查改来思考

  • 增:链式存储计算上优于顺序存储;中间插入不用移动位置
  • 删:链式存储计算上优于顺序存储;中间插入不用移动位置
  • 查:顺序存储使用上优于链式存储;链式只能遍历,顺序可以下标直接表示
  • 改:顺序存储使用上优于链式存储;链式只能遍历,顺序可以下标直接表示

LinkedList

LinkedList是什么

是c#为我们封装好的类,本质是一个可变类型的泛型双向链表

申明

1
2
using System.Collections.Generic
LinkedList<int> link = new LinkedList<int>();

增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//加在链表尾部
link.AddLast(10);
//加在链表头部
link.AddFirst(20);

//删除
link.RemoveFirst();//移除头节点
link.RemoveLast();//移除尾节点
link.Remove(20);
link.Clear();

//查
//头节点
LinkListNode<int> first = link.First;
//尾节点
LinkListNode<int> last = link.Last;
//无法直接通过下标寻找,只能遍历查找指定位置元素
LinkListNode<int> node = link.Find(10);
LinkListNode<int> n = link.Find(20);
link.AddAfter(n,15);
link.AddBefore(n,11);
//判断是否存在
if(link.Contains(10))
{

}

//改
link.First.Value = 11;

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
foreach(int item in link)
{
Console.WriteLine(item);
}

LinkListNode<int> now = LinkList.First
while(now != null)
{
now = now.Next;
}

LinkListNode<int> now = LinkList.Last
while(now != null)
{
now = now.Previous;
}

泛型栈和队列

使用起来没什么不一样,

1
2
Stack<int> stack = new Stack<int>();
Queue<int> queue = new Queue<int>();